/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "keyboard_def.h"
#include "hilog_wrapper.h"
#include "js_native_api.h"
#include "keyboard_utils.h"
#include "napi/native_api.h"
#include "cashdevice_napi.h"
#include "keyboard_observer.h"
#include "napi/native_common.h"
#include "keyboard_srv_client.h"
#include "js_native_api_types.h"
#include <cstdio>
#include <cstdlib>
#include <string>
#include <string.h>

using namespace SwanLink::Finance;
thread_local static KeyboardSrvClient& g_keyboardClient = KeyboardSrvClient::GetInstance();
static KeyboardObserver  *g_pinListener = nullptr;

/**
 * @brief 定义接口函数
 * static napi_value XXXX(napi_env env, napi_callback_info info)
 * @param env 
 * @param info 
 * @return napi_value 
 */
static napi_value IsSM4Enabled(napi_env env, napi_callback_info info) {
    napi_value this_arg = nullptr;
    void *priv_data = nullptr;
    size_t argc = 1;
    napi_value args[1] = {0};
    struct tTmpStruct{
        int handle;
        bool ret;
    };

    tTmpStruct *pVar = new tTmpStruct();
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
    NAPI_CALL(env, napi_get_value_int32(env, args[0], &pVar->handle));
    int &handle = pVar->handle;
    bool &ret = pVar->ret;
    ret = g_keyboardClient.IsSM4Enabled(handle);
    napi_value result;
    NAPI_CALL(env, napi_get_boolean(env, pVar->ret, &result));

    return result;
}
static napi_value GetSN(napi_env env, napi_callback_info info) {
    napi_value this_arg = nullptr;
    void *priv_data = nullptr;
    size_t argc = 1;
    napi_value args[1] = {0};
    struct tTmpStruct{
        int handle;
        char sn[1024];
        int len;
        int ret;
    };
    tTmpStruct *pVar = new tTmpStruct();
    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    NAPI_CALL(env, napi_get_value_int32(env, args[0], &pVar->handle));
    int &handle = pVar->handle;
    char* sn = pVar->sn;
    int &len = pVar->len;
    int &ret = pVar->ret;
    ret = g_keyboardClient.GetSN(handle, sn, len);
    KEYBOARD_HILOGD(KEYBOARD_JS_NAPI, "sn : %{public}s, len: %{public}d, status : %{public}d", sn, len, ret);
    napi_value result;
    (void)pVar->ret;
    NAPI_CALL(env, napi_create_string_utf8(env, pVar->sn, strlen(pVar->sn), &result));

    return result;
}
static napi_value CalculateMAC(napi_env env, napi_callback_info info) {
    napi_value this_arg = nullptr;
    void *priv_data = nullptr;
    size_t argc = 4;
    napi_value args[4] = {0};
    int handle;
    KeyInfo key;
    char plain[2048];
    size_t len;
    char enc_data[2048];
    int enc_len;
    int ret;

    DEBUG_NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    DEBUG_NAPI_CALL(env, napi_get_value_int32(env, args[0], &handle));
    NAPI_OBJECT_PROP_GET(args[1], int32, "keyID", (int32_t *)&key.keyID);
    NAPI_OBJECT_PROP_GET(args[1], int32, "keyType", (int32_t *)&key.keyType);
    NAPI_OBJECT_PROP_GET(args[1], int32, "user_key_type", (int32_t *)&key.user_key_type);
    DEBUG_NAPI_CALL(env, napi_get_value_string_utf8(env, args[2], plain, 2048, &len));

    ret = g_keyboardClient.CalculateMAC(handle, key, plain, len, enc_data, enc_len);
    napi_value result;
    NAPI_CALL(env, napi_create_object(env, &result));
    NAPI_OBJECT_PROP_SET(result, int32, "status", ret);
    if (enc_len > 0) {
       NAPI_OBJECT_PROP_SET_BUFFER(result, "data", enc_len, enc_data);
    } else {
        napi_value null_ret;
        napi_create_string_utf8(env, "", NAPI_AUTO_LENGTH, &null_ret);
        napi_set_named_property(env, result, "data", null_ret);
    }

    return result;
}
static napi_value EncryptData(napi_env env, napi_callback_info info) {
    napi_value this_arg = nullptr;
    void *priv_data = nullptr;
    size_t argc = 4;
    napi_value args[4] = {0};
    int handle;
    KeyInfo key;
    char plain[520] = {0};
    size_t len;
    char enc_data[2048] = {0};
    int enc_len;
    int ret;

    DEBUG_NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    DEBUG_NAPI_CALL(env, napi_get_value_int32(env, args[0], &handle));
    NAPI_OBJECT_PROP_GET(args[1], int32, "keyID", (int32_t *)&key.keyID);
    NAPI_OBJECT_PROP_GET(args[1], int32, "keyType", (int32_t *)&key.keyType);
    NAPI_OBJECT_PROP_GET(args[1], int32, "user_key_type", (int32_t *)&key.user_key_type);
    DEBUG_NAPI_CALL(env, napi_get_value_string_utf8(env, args[2], plain, 520, &len));

    ret = g_keyboardClient.EncryptData(handle, key, plain, len, enc_data, enc_len);
    KEYBOARD_HILOGD(KEYBOARD_JS_NAPI, "plain : %{public}s, len: %{public}d, enc : %{public}s, len : %{public}d",
                    plain, (int)len, enc_data, enc_len);
    napi_value result;
    NAPI_CALL(env, napi_create_object(env, &result));
    NAPI_OBJECT_PROP_SET(result, int32, "status", ret);
    if (enc_len > 0) {
       NAPI_OBJECT_PROP_SET_BUFFER(result, "data", enc_len, enc_data);
    } else {
            napi_value null_ret;
            napi_create_string_utf8(env, "", NAPI_AUTO_LENGTH, &null_ret);
            napi_set_named_property(env, result, "data", null_ret);
    }

    return result;
}

static napi_value SetPINLength(napi_env env, napi_callback_info info) {
    napi_value this_arg = nullptr;
    void *priv_data = nullptr;
    size_t argc = 3;
    napi_value args[3] = {0};
    int handle;
    int minLen;
    int maxLen;
    int ret;

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    NAPI_ASSERT(env, argc >= 3, "Wrong number of arguments");
    NAPI_CALL(env, napi_get_value_int32(env, args[0], &handle));
    NAPI_CALL(env, napi_get_value_int32(env, args[1], &minLen));
    NAPI_CALL(env, napi_get_value_int32(env, args[2], &maxLen));
    ret = g_keyboardClient.SetPINLength(handle, minLen, maxLen);
    napi_value result;
    NAPI_CALL(env, napi_create_int32(env, ret, &result));

    return result;
}
static napi_value SetTextMode(napi_env env, napi_callback_info info) {
    napi_value this_arg = nullptr;
    void *priv_data = nullptr;
    size_t argc = 2;
    napi_value args[2] = {0};
    struct tTmpStruct{
        int handle;
        TextMode mode;
        int ret;
    };

    tTmpStruct *pVar = new tTmpStruct();

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    NAPI_ASSERT(env, argc >= 2, "Wrong number of arguments");
    NAPI_CALL(env, napi_get_value_int32(env, args[0], &pVar->handle));
    NAPI_CALL(env, napi_get_value_int32(env, args[1], reinterpret_cast<int32_t*>(&pVar->mode)));
    async_excute_work work_func = [](napi_env env, size_t argc, napi_value args[], void *data)->napi_status {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct *>(data);
        int &handle = pVar->handle;
        TextMode &mode = pVar->mode;
        pVar->ret = g_keyboardClient.SetTextMode(handle, mode);

        return napi_ok;
    };
    async_result_work result_func = [] (napi_env env, void *data)->napi_value {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct *>(data);
        napi_value result;
        NAPI_CALL(env, napi_create_int32(env, pVar->ret, &result));

        return result;
    };
    async_free_work free_func = [] (napi_env env, void *data) {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct *>(data);
        delete pVar;
    };
    AsyncCallback *async = new AsyncCallback(env, argc, args, work_func, result_func, free_func,
        pVar,
        this_arg,
        priv_data);
    return NapiCall(env, async);
}

static napi_value ClearTextMode(napi_env env, napi_callback_info info) {
    napi_value this_arg = nullptr;
    void *priv_data = nullptr;
    size_t argc = 1;
    napi_value args[1] = {0};
    struct tTmpStruct{
        int handle;
    };

    tTmpStruct *pVar = new tTmpStruct();

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
    NAPI_CALL(env, napi_get_value_int32(env, args[0], &pVar->handle));

    async_excute_work work_func = [](napi_env env, size_t argc, napi_value args[], void *data)->napi_status {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct *>(data);
        int &handle = pVar->handle;
        g_keyboardClient.ClearTextMode(handle);

        return napi_ok;
    };
    async_result_work result_func = [] (napi_env env, void *data)->napi_value {
        napi_value result;
        NAPI_CALL(env, napi_get_undefined(env, &result));

        return result;
    };
    async_free_work free_func = [] (napi_env env, void *data) {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct *>(data);

        delete pVar;
    };
    AsyncCallback *async = new AsyncCallback(env, argc, args, work_func, result_func, free_func,
        pVar,
        this_arg,
        priv_data);
    return NapiCall(env, async);
}
static napi_value LoadMasterKey(napi_env env, napi_callback_info info)
{
    napi_value this_arg = nullptr;
    void *priv_data = nullptr;
    size_t argc = 4;
    napi_value args[4] = {0};
    int handle;
    KeyID masterKeyID;
    unsigned char KeyValue[256];
    int ret;

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    NAPI_ASSERT(env, argc >= 3, "Wrong number of arguments");
    DEBUG_NAPI_CALL(env, napi_get_value_int32(env, args[0], &handle));
    DEBUG_NAPI_CALL(env, napi_get_value_int32(env, args[1], reinterpret_cast<int32_t *>(&masterKeyID)));
    size_t KeyValueLen = 0;
    DEBUG_NAPI_CALL(env, napi_get_value_string_utf8(env, args[2],
                    reinterpret_cast<char *>(KeyValue), 256, &KeyValueLen));
    ret = g_keyboardClient.LoadMasterKey(handle, masterKeyID, KeyValue);
    KEYBOARD_HILOGD(KEYBOARD_JS_NAPI, "masterKeyID : %{public}d, KeyValue : %{public}s",
            masterKeyID, KeyValue);
    napi_value result;
    DEBUG_NAPI_CALL(env, napi_create_int32(env, ret, &result));

    return result;
}
static napi_value LoadUserKey(napi_env env, napi_callback_info info)
{
    napi_value this_arg = nullptr;
    void *priv_data = nullptr;
    size_t argc = 4;
    napi_value args[4] = {0};
    int handle;
    KeyID masterKeyID;
    userKeyType user_Key_Type;
    unsigned char KeyValue[256];
    int ret;

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    NAPI_ASSERT(env, argc >= 4, "Wrong number of arguments");
    DEBUG_NAPI_CALL(env, napi_get_value_int32(env, args[0], &handle));
    DEBUG_NAPI_CALL(env, napi_get_value_int32(env, args[1], reinterpret_cast<int32_t *>(&masterKeyID)));
    DEBUG_NAPI_CALL(env, napi_get_value_int32(env, args[2], reinterpret_cast<int32_t *>(&user_Key_Type)));
    size_t KeyValueLen = 0;
    DEBUG_NAPI_CALL(env, napi_get_value_string_utf8(env, args[3],
                    reinterpret_cast<char*>(KeyValue), 256, &KeyValueLen));
    ret = g_keyboardClient.LoadUserKey(handle, masterKeyID, user_Key_Type, KeyValue);
    KEYBOARD_HILOGD(KEYBOARD_JS_NAPI, "masterKeyID : %{public}d, user_Key_Type: %{public}d, KeyValue : %{public}s",
            masterKeyID, user_Key_Type, KeyValue);
    napi_value result;
    DEBUG_NAPI_CALL(env, napi_create_int32(env, ret, &result));

    return result;
}
static napi_value UpdateMasterKey(napi_env env, napi_callback_info info) {
     napi_value this_arg = nullptr;
    void *priv_data = nullptr;
    size_t argc = 4;
    napi_value args[4] = {0};
    int handle;
    KeyID masterKeyID;
    unsigned char KeyValue[256];
    int ret;

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    NAPI_ASSERT(env, argc >= 3, "Wrong number of arguments");
    DEBUG_NAPI_CALL(env, napi_get_value_int32(env, args[0], &handle));
    DEBUG_NAPI_CALL(env, napi_get_value_int32(env, args[1], reinterpret_cast<int32_t *>(&masterKeyID)));
    size_t KeyValueLen = 0;
    DEBUG_NAPI_CALL(env, napi_get_value_string_utf8(env, args[2],
                    reinterpret_cast<char *>(KeyValue), 256, &KeyValueLen));
    ret = g_keyboardClient.UpdateMasterKey(handle, masterKeyID, KeyValue);
    KEYBOARD_HILOGD(KEYBOARD_JS_NAPI, "masterKeyID : %{public}d, KeyValue : %{public}s", masterKeyID, KeyValue);
    napi_value result;
    DEBUG_NAPI_CALL(env, napi_create_int32(env, ret, &result));

    return result;
}
static napi_value UpdateUserKey(napi_env env, napi_callback_info info)
{
    napi_value this_arg = nullptr;
    void *priv_data = nullptr;
    size_t argc = 4;
    napi_value args[4] = { 0 };
    int handle;
    KeyID masterKeyID;
    userKeyType user_Key_Type;
    unsigned char KeyValue[256];
    int ret;

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    NAPI_ASSERT(env, argc >= 4, "Wrong number of arguments");
    DEBUG_NAPI_CALL(env, napi_get_value_int32(env, args[0], &handle));
    DEBUG_NAPI_CALL(env, napi_get_value_int32(env, args[1], reinterpret_cast<int32_t *>(&masterKeyID)));
    DEBUG_NAPI_CALL(env, napi_get_value_int32(env, args[2], reinterpret_cast<int32_t *>(&user_Key_Type)));
    size_t KeyValueLen = 0;
    DEBUG_NAPI_CALL(env, napi_get_value_string_utf8(env, args[3],
                    reinterpret_cast<char*>(KeyValue), 256, &KeyValueLen));
    ret = g_keyboardClient.LoadUserKey(handle, masterKeyID, user_Key_Type, KeyValue);
    KEYBOARD_HILOGD(KEYBOARD_JS_NAPI, "masterKeyID : %{public}d, user_Key_Type: %{public}d, KeyValue : %{public}s",
            masterKeyID, user_Key_Type, KeyValue);
    napi_value result;
    DEBUG_NAPI_CALL(env, napi_create_int32(env, ret, &result));

    return result;
}
static napi_value DeleteUserKey(napi_env env, napi_callback_info info)
{
    napi_value this_arg = nullptr;
    void *priv_data = nullptr;
    size_t argc = 3;
    napi_value args[3] = { 0 };
    int handle;
    KeyID masterKeyID;
    userKeyType user_Key_Type;
    int ret;

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    DEBUG_NAPI_CALL(env, napi_get_value_int32(env, args[0], &handle));
    DEBUG_NAPI_CALL(env, napi_get_value_int32(env, args[1], reinterpret_cast<int32_t *>(&masterKeyID)));
    DEBUG_NAPI_CALL(env, napi_get_value_int32(env, args[2], reinterpret_cast<int32_t *>(&user_Key_Type)));
    ret = g_keyboardClient.DeleteUserKey(handle, masterKeyID, user_Key_Type);
    KEYBOARD_HILOGD(KEYBOARD_JS_NAPI, "masterKeyID : %{public}d, user_Key_Type:  %{public}d",
            masterKeyID, user_Key_Type);
    napi_value result;
    DEBUG_NAPI_CALL(env, napi_create_int32(env, ret, &result));

    return result;
}
static napi_value ListenForPin(napi_env env, napi_callback_info info)
{
    napi_value this_arg = nullptr;
    void *priv_data = nullptr;
    size_t argc = 3;
    napi_value args[3] = {0};
    struct tTmpStruct{
        int handle;
        PinListener* listener;
        int timeout;
        int buf_len;
        char key_buf[64];
    };

    tTmpStruct *pVar = new tTmpStruct();

    NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &this_arg, &priv_data));
    NAPI_CALL(env, napi_get_value_int32(env, args[0], &pVar->handle));
    NAPI_CALL(env, napi_get_value_int32(env, args[1], &pVar->timeout));
    if (!g_pinListener) {
        g_pinListener = new KeyboardObserver();
    }
    g_pinListener->RegisterObserver(env, args[2]);
    pVar->listener = g_pinListener;

    async_excute_work work_func = [](napi_env env, size_t argc, napi_value args[], void *data)->napi_status {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct *>(data);
        int& handle = pVar->handle;
        PinListener* listener = pVar->listener;
        int& timeout = pVar->timeout;
        pVar->buf_len = 0;
        g_keyboardClient.ListenForPin(handle, listener, timeout);
        return napi_ok;
    };
    async_result_work result_func = [] (napi_env env, void *data)->napi_value {
        return nullptr;
    };
    async_free_work free_func = [] (napi_env env, void *data) {
        tTmpStruct *pVar = reinterpret_cast<tTmpStruct *>(data);
        delete pVar;
    };
    AsyncCallback *async = new AsyncCallback(env, argc, args, work_func, result_func, free_func,
        pVar,
        this_arg,
        priv_data);

    return AsyncCall(env, nullptr, "ListenForPin", async);
}


struct AddonData {
  napi_async_work asyncWork = nullptr;
  napi_deferred deferred = nullptr;
  napi_ref callback = nullptr;
  double args[2] = {0};
  double result = 0;
};

// 业务逻辑处理函数，由worker线程池调度执行。
static void addExecuteCB(napi_env env, void *data) {
  AddonData *addonData = reinterpret_cast<AddonData *>(data);

  // 执行复杂计算，不阻塞主线程。此处用一个加法简单示意。
  addonData->result = addonData->args[0] + addonData->args[1];
}

// 业务逻辑处理完成回调函数，在业务逻辑处理函数执行完成或取消后触发，由EventLoop线程中执行。
static void addCallbackCompleteCB(napi_env env, napi_status status, void *data) {
  AddonData *addonData = reinterpret_cast<AddonData *>(data);
  napi_value callback = nullptr;
  napi_get_reference_value(env, addonData->callback, &callback);
  napi_value undefined = nullptr;
  napi_get_undefined(env, &undefined);
  napi_value result = nullptr;
  napi_create_double(env, addonData->result, &result);
  napi_value callbackResult = nullptr;

  // 执行回调函数
  napi_call_function(env, undefined, callback, 1, &result, &callbackResult);

  // 删除napi_ref对象
  if (addonData->callback != nullptr) {
    napi_delete_reference(env, addonData->callback);
  }

  // 删除异步工作项
  napi_delete_async_work(env, addonData->asyncWork);

  delete addonData;
}

static napi_value addCallback(napi_env env, napi_callback_info info) {
  // 获取3个参数，值的类型是js类型（napi_value）
  size_t argc = 3;
  napi_value args[3];
  napi_value thisArg = nullptr;
  NAPI_CALL(env, napi_get_cb_info(env, info, &argc, args, &thisArg, nullptr));

  // 获取并判断js参数类型
  napi_valuetype valuetype0;
  NAPI_CALL(env, napi_typeof(env, args[0], &valuetype0));
  napi_valuetype valuetype1;
  NAPI_CALL(env, napi_typeof(env, args[1], &valuetype1));
  if (valuetype0 != napi_number || valuetype1 != napi_number) {
    napi_throw_type_error(env, nullptr, "Wrong arguments. 2 numbers expected.");

    return NULL;
  }

  napi_valuetype valuetype2;
  NAPI_CALL(env, napi_typeof(env, args[2], &valuetype2));
  if (valuetype2 != napi_function) {
    napi_throw_type_error(env, nullptr, "Callback function expected.");
    return NULL;
  }

  // 异步工作项上下文用户数据，传递到异步工作项的execute、complete中传递数据
  auto addonData = new AddonData{
      .asyncWork = nullptr,
  };

  // 将接收到的参数传入用户自定义上下文数据
  NAPI_CALL(env, napi_get_value_double(env, args[0], &addonData->args[0]));
  NAPI_CALL(env, napi_get_value_double(env, args[1], &addonData->args[1]));
  NAPI_CALL(env, napi_create_reference(env, args[2], 1, &addonData->callback));

  // 创建async work，创建成功后通过最后一个参数接收async work的handle
  napi_value resourceName = nullptr;
  napi_create_string_utf8(env, "addCallback", NAPI_AUTO_LENGTH, &resourceName);
  napi_create_async_work(env, nullptr, resourceName, addExecuteCB, addCallbackCompleteCB,
                        reinterpret_cast<void *>(addonData), &addonData->asyncWork);

  // 将刚创建的async work加到队列，由底层去调度执行
  napi_queue_async_work(env, addonData->asyncWork);

  // 原生方法返回空对象
  napi_value result = 0;
  NAPI_CALL(env, napi_get_null(env, &result));
  return result;
}


/**
 * @brief 定义枚举类型
 */
static napi_value EnumClassConstructor(napi_env env, napi_callback_info info) {
    napi_value thisArg = nullptr;
    void* data = nullptr;
    napi_get_cb_info(env, info, nullptr, nullptr, &thisArg, &data);
    napi_value global = nullptr;
    napi_get_global(env, &global);

    return thisArg;
}

static napi_value CreateEnumKeyType(napi_env env, napi_value exports) {
    napi_value values[3] = {0};
    napi_create_int32(env, (int32_t)KEY_TYPE_SM4, &values[0]);
    napi_create_int32(env, (int32_t)KEY_TYPE_RSA, &values[1]);
    napi_create_int32(env, (int32_t)KEY_TYPE_DES, &values[2]);
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_STATIC_PROPERTY("KEY_TYPE_SM4", values[0]),
        DECLARE_NAPI_STATIC_PROPERTY("KEY_TYPE_RSA", values[1]),
        DECLARE_NAPI_STATIC_PROPERTY("KEY_TYPE_DES", values[2]),
    };
    napi_value result = nullptr;
    napi_define_class(env, "KeyType", NAPI_AUTO_LENGTH, EnumClassConstructor, nullptr, 3, desc, &result);
    napi_set_named_property(env, exports, "KeyType", result);

    return exports;
}

static napi_value CreateEnumUserKeyType(napi_env env, napi_value exports) {
    napi_value values[3] = {0};
    napi_create_int32(env, (int32_t)USER_KEY_ID_PIN, &values[0]);
    napi_create_int32(env, (int32_t)USER_KEY_ID_MAC, &values[1]);
    napi_create_int32(env, (int32_t)USER_KEY_ID_DATA, &values[2]);
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_STATIC_PROPERTY("USER_KEY_ID_PIN", values[0]),
        DECLARE_NAPI_STATIC_PROPERTY("USER_KEY_ID_MAC", values[1]),
        DECLARE_NAPI_STATIC_PROPERTY("USER_KEY_ID_DATA", values[2]),
    };
    napi_value result = nullptr;
    napi_define_class(env, "userKeyType", NAPI_AUTO_LENGTH, EnumClassConstructor, nullptr, 3, desc, &result);
    napi_set_named_property(env, exports, "userKeyType", result);

    return exports;
}

static napi_value CreateEnumKeyPress(napi_env env, napi_value exports) {
    napi_value values[5] = {0};
    napi_create_int32(env, (int32_t)KEY_CHAR, &values[0]);
    napi_create_int32(env, (int32_t)KEY_SHARP, &values[1]);
    napi_create_int32(env, (int32_t)KEY_RETURN, &values[2]);
    napi_create_int32(env, (int32_t)KEY_CLEAR, &values[3]);
    napi_create_int32(env, (int32_t)KEY_EXIT, &values[4]);
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_STATIC_PROPERTY("KEY_CHAR", values[0]),
        DECLARE_NAPI_STATIC_PROPERTY("KEY_SHARP", values[1]),
        DECLARE_NAPI_STATIC_PROPERTY("KEY_RETURN", values[2]),
        DECLARE_NAPI_STATIC_PROPERTY("KEY_CLEAR", values[3]),
        DECLARE_NAPI_STATIC_PROPERTY("KEY_EXIT", values[4]),
    };
    napi_value result = nullptr;
    napi_define_class(env, "KeyPress", NAPI_AUTO_LENGTH, EnumClassConstructor, nullptr, 5, desc, &result);
    napi_set_named_property(env, exports, "KeyPress", result);

    return exports;
}

static napi_value CreateEnumMacAlg(napi_env env, napi_value exports) {
    napi_value values[7] = {0};
    napi_create_int32(env, (int32_t)ALG_MAC_METHOD_X919_80, &values[0]);
    napi_create_int32(env, (int32_t)ALG_MAC_METHOD_ECB, &values[1]);
    napi_create_int32(env, (int32_t)ALG_MAC_METHOD_X919_X00, &values[2]);
    napi_create_int32(env, (int32_t)ALG_MAC_METHOD_BOCE, &values[3]);
    napi_create_int32(env, (int32_t)ALG_MAC_METHOD_X919_00, &values[4]);
    napi_create_int32(env, (int32_t)ALG_MAC_METHOD_XOR_3DES, &values[5]);
    napi_create_int32(env, (int32_t)ALG_MAC_METHOD_X99, &values[6]);
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_STATIC_PROPERTY("ALG_MAC_METHOD_X919_80", values[0]),
        DECLARE_NAPI_STATIC_PROPERTY("ALG_MAC_METHOD_ECB", values[1]),
        DECLARE_NAPI_STATIC_PROPERTY("ALG_MAC_METHOD_X919_X00", values[2]),
        DECLARE_NAPI_STATIC_PROPERTY("ALG_MAC_METHOD_BOCE", values[3]),
        DECLARE_NAPI_STATIC_PROPERTY("ALG_MAC_METHOD_X919_00", values[4]),
        DECLARE_NAPI_STATIC_PROPERTY("ALG_MAC_METHOD_XOR_3DES", values[5]),
        DECLARE_NAPI_STATIC_PROPERTY("ALG_MAC_METHOD_X99", values[6]),
    };
    napi_value result = nullptr;
    napi_define_class(env, "MacAlg", NAPI_AUTO_LENGTH, EnumClassConstructor, nullptr, 7, desc, &result);
    napi_set_named_property(env, exports, "MacAlg", result);

    return exports;
}
static napi_value CreateEnumTextMode(napi_env env, napi_value exports) {
    napi_value values[2] = {0};
    napi_create_int32(env, (int32_t)TEXT_MODE_PLAIN, &values[0]);
    napi_create_int32(env, (int32_t)TEXT_MODE_ENCRYPT, &values[1]);
    napi_property_descriptor desc[] = {
        DECLARE_NAPI_STATIC_PROPERTY("TEXT_MODE_PLAIN", values[0]),
        DECLARE_NAPI_STATIC_PROPERTY("TEXT_MODE_ENCRYPT", values[1]),
    };
    napi_value result = nullptr;
    napi_define_class(env, "TextMode", NAPI_AUTO_LENGTH, EnumClassConstructor, nullptr, 2, desc, &result);
    napi_set_named_property(env, exports, "TextMode", result);

    return exports;
}

static napi_value CreateEnumStatusCode(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        DECLARE_CONST_VAR(ERROR_KEYBOARD_DEVICE_NOT_SUPPORTED),
        DECLARE_CONST_VAR(ERROR_KEYBOARD_DEVICE_NON_EXISTENT),
        DECLARE_CONST_VAR(ERROR_KEYBOARD_OPEN_FAIL),
        DECLARE_CONST_VAR(ERROR_KEYBOARD_KEY_SET),
        DECLARE_CONST_VAR(ERR_GET_SN),
        DECLARE_CONST_VAR(ERR_LOAD_KEY),
        DECLARE_CONST_VAR(ERR_GET_MAC),
        DECLARE_CONST_VAR(ERR_UPDATE),
        DECLARE_CONST_VAR(ERR_VALUE),
        DECLARE_CONST_VAR(ERROR_KEYBOARD_DEVICE_NOT_OPEN),
        DECLARE_CONST_VAR(ERR_KEYBOARD_HW_ERROR),
        DECLARE_CONST_VAR(DEVICE_DISCONNECTED),
        DECLARE_CONST_VAR(ERR_KEY_VAULE_NULL),
        DECLARE_CONST_VAR(ERR_DATA_OUT_OF_RANGE),
        DECLARE_CONST_VAR(ERR_KEY_ID_OUT_OF_RANGE),
        DECLARE_CONST_VAR(ERR_KEY_TYPE_OUT_OF_RANGE),
        DECLARE_CONST_VAR(ERR_PLAIN_DATA_NULL),
        DECLARE_CONST_VAR(ERR_KEY_USER_TYPE_OUT_OF_RANGE),
        DECLARE_CONST_VAR(ERR_ENCRYPT_DATA_FAIL),
        DECLARE_CONST_VAR(ERR_SET_PINLEN_DATA),
        DECLARE_CONST_VAR(ERR_DEVICE_NOT_ACTIVE),
        DECLARE_CONST_VAR(ERR_DEVICE_ACTIVE_FAIL),
        DECLARE_CONST_VAR(ERR_KEYBOARD_INPUT_TIME_OUT),
        DECLARE_CONST_VAR(ERR_KEYBOARD_READKEY_FAILED),
        DECLARE_CONST_VAR(ERR_KEYBOARD_READKEY_UNKNOWN),
        DECLARE_CONST_VAR(ERR_KEYBOARD_LENGTH_TOO_SHORT),
        DECLARE_CONST_VAR(ERR_KEYBOARD_LENGTH_MECISM),
        DECLARE_CONST_VAR(ERR_KEYBOARD_INVALID_KEY),
        DECLARE_CONST_VAR(ERR_KEYBOARD_OUT_OF_RANGE),
        DECLARE_CONST_VAR(ERR_KEYBOARD_TIME_OUT_OF_RANGE),
        DECLARE_CONST_VAR(ERR_NOT_SUPPORTED_LOAD_MASRTKEYID),
        DECLARE_CONST_VAR(ERR_SET_KEYBOARD_SM_FAILE),
        DECLARE_CONST_VAR(ERR_GET_DATA_FAIL),
        DECLARE_CONST_VAR(ERR_KEYBOARD_INPUT_KEYVALUE_SHORT),
    };
    napi_value result;
    bool has = false;
    napi_has_named_property(env, exports, "StatusCode", &has);
    if (!has) {
        napi_define_class(env,  "StatusCode",  NAPI_AUTO_LENGTH,  EnumClassConstructor,
        nullptr, sizeof(desc)/sizeof(desc[0]), desc, &result);
        napi_set_named_property(env, exports, "StatusCode", result);
    } else {
        napi_get_named_property(env, exports, "StatusCode", &result);
        for (int i = 0; i < sizeof(desc)/sizeof(desc[0]); i++) {
            napi_set_named_property(env, result, desc[i].utf8name, desc[i].value);
        }
    }

    return result;
}

EXTERN_C_START
/*
 * function for module exports
 */
static napi_value KeyboardInit(napi_env env, napi_value exports) {
    KEYBOARD_HILOGD(KEYBOARD_JS_NAPI, "Enter");
    Common::ClientInit(&g_keyboardClient, env, exports);
    napi_property_descriptor desc[] = {
    DECLARE_NAPI_FUNCTION("GetSupportAccess", Common::GetSupportAccess),
    DECLARE_NAPI_FUNCTION("Open", Common::Open),
    DECLARE_NAPI_FUNCTION("Close", Common::Close),
    DECLARE_NAPI_FUNCTION("QueryStatus", Common::QueryStatus),
    DECLARE_NAPI_FUNCTION("Reset", Common::Reset),
    DECLARE_NAPI_FUNCTION("IsSM4Enabled", IsSM4Enabled),
    DECLARE_NAPI_FUNCTION("GetSN", GetSN),
    DECLARE_NAPI_FUNCTION("CalculateMAC", CalculateMAC),
    DECLARE_NAPI_FUNCTION("EncryptData", EncryptData),
    DECLARE_NAPI_FUNCTION("SetPINLength", SetPINLength),
    DECLARE_NAPI_FUNCTION("SetTextMode", SetTextMode),
    DECLARE_NAPI_FUNCTION("ClearTextMode", ClearTextMode),
    DECLARE_NAPI_FUNCTION("LoadMasterKey", LoadMasterKey),
    DECLARE_NAPI_FUNCTION("LoadUserKey", LoadUserKey),
    DECLARE_NAPI_FUNCTION("UpdateMasterKey", UpdateMasterKey),
    DECLARE_NAPI_FUNCTION("UpdateUserKey", UpdateUserKey),
    DECLARE_NAPI_FUNCTION("DeleteUserKey", DeleteUserKey),
    DECLARE_NAPI_FUNCTION("ListenForPin", ListenForPin),
    DECLARE_NAPI_FUNCTION("addCallback", addCallback)
    };
    napi_value keyboard;
    napi_create_object(env, &keyboard);
    NAPI_CALL(env, napi_define_properties(env, keyboard, sizeof(desc)/sizeof(desc[0]) , desc));
    napi_set_named_property(env, exports, "Keyboard", keyboard);
    CreateEnumKeyType(env, exports);
    CreateEnumUserKeyType(env, exports);
    CreateEnumMacAlg(env, exports);
    CreateEnumTextMode(env, exports);
    CreateEnumStatusCode(env, exports);
    CreateEnumKeyPress(env, exports);

    KEYBOARD_HILOGD(KEYBOARD_JS_NAPI, "Success");

    return exports;
}
EXTERN_C_END

/*
 * Module definition
 */
static napi_module g_module = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = "keyboard",
    .nm_register_func = KeyboardInit,
    .nm_modname = "keyboard",
    .nm_priv = ((void*)0),
    .reserved = {0}
};

/*
 * Module registration
 */
extern "C" __attribute__((constructor)) void RegisterModule(void) {
    napi_module_register(&g_module);
}
